#include "syscore.h"
#include "timer.h"
#include "syscore.x"


/*
 NOTES:   
 1. pTimerContext should be allocated first
 2. pTimerMemory should be allocated first
*/
RESULT TIMERS_init(PTIMERCONTEXT pTimerContext,void* pTimerMemory,U32 resolution )
{
    if((pTimerContext == NULL) || (pTimerMemory == NULL) )
        return RC_FAIL;
    if(SYS_lock_init(&pTimerContext->lock) != RC_OK)
        return RC_FAIL;

    pTimerContext->resolution = resolution;
    pTimerContext->pTimerList = NULL;
    pTimerContext->pTimerMemory = pTimerMemory;
    return RC_OK;
    
}

RESULT TIMERS_destory(PTIMERCONTEXT pTimerContext )
{
    if(pTimerContext == NULL) 
        return RC_FAIL;

    if(SYS_lock_destory(&pTimerContext->lock) != RC_OK)
        return RC_FAIL;

    pTimerContext->pTimerList = NULL;
    pTimerContext->pTimerMemory = NULL;
    return RC_OK;
    
}

static void TIMERS_InsertTimerInList(PTIMERCONTEXT pTimerContext,PTIMERINFO pTimerInfo)
{
	PTIMERINFO pTimerLast = pTimerContext->pTimerList;

	if(pTimerLast == NULL)
	{// No timer in list yet - add timer
		pTimerContext->pTimerList = pTimerInfo;
	}
	else
	{// Get the last timer struct
		while(pTimerLast->Next)
			pTimerLast = pTimerLast->Next;

		pTimerLast->Next = pTimerInfo;
		pTimerInfo->Prev = pTimerLast;
	}

	pTimerInfo->state = TIMER_ON;

}


static void TIMERS_RemoveTimerFromList(PTIMERCONTEXT pTimerContext,PTIMERINFO pTimerInfo)
{
	PTIMERINFO pTimerInfoPrev = pTimerInfo->Prev;
	PTIMERINFO pTimerInfoNext = pTimerInfo->Next;

	if(pTimerInfo->state == TIMER_FREE)
	{// Timer already released - This should never happened or else it's a bug in the feature using the timers
        dbgprintf(DEBUG_ERROR,stderr,"Timer already freed %p\n",pTimerInfo);
		return;
	}

	if(pTimerInfoPrev == NULL)
	{// This timer struct was the first of the list
		pTimerContext->pTimerList = pTimerInfoNext;
	}
	else
	{
		pTimerInfoPrev->Next = pTimerInfoNext;
	}

	if(pTimerInfoNext != NULL)
	{
		pTimerInfoNext->Prev = pTimerInfoPrev;
	}

	pTimerInfo->state = TIMER_FREE;

	// Release timer struct
	SYS_freeMemArray((PMEMARRAY)pTimerContext->pTimerMemory, pTimerInfo);
}




TIMERINFO* TIMERS_start_timer(PTIMERCONTEXT pTimerContext, U32 msTime, CALLBACK cb, void *Param)
{
	PTIMERINFO pTimerInfo;

	// Null Callback should NOT be supported (anyway - what would be the use of a timer then ?!?)
	if(cb == NULL)
	{
        dbgprintf(DEBUG_ERROR,stderr,"NULL CallBack\n",NULL);
		return NULL;
	}

	// Alloc a Timer
	if((pTimerInfo =(TIMERINFO*)SYS_allocMemArray((PMEMARRAY)pTimerContext->pTimerMemory)) == NULL)
	{
        dbgprintf(DEBUG_ERROR,stderr,"No memory\n",NULL);
		return NULL;
	}

	memset((void*)pTimerInfo, 0, sizeof(TIMERINFO));

	pTimerInfo->timeRemaining = msTime;
	pTimerInfo->timerCb = cb;
	pTimerInfo->Param = Param;

	// Insert timer in list
	TIMERS_InsertTimerInList(pTimerContext,pTimerInfo);

	return pTimerInfo;
}

void TIMERS_clear_timer(PTIMERCONTEXT pTimerContext,PTIMERINFO pTimerInfo)
{
	if (pTimerInfo)
	{
		// Remove timer from list
		TIMERS_RemoveTimerFromList(pTimerContext,pTimerInfo);
	}
}

void TIMERS_restart_timer(PTIMERINFO pTimerInfo, U32 msTime)
{
	if (pTimerInfo)
	{
		pTimerInfo->state = TIMER_ON;        
		pTimerInfo->timeRemaining = msTime;
	}
}


void TIMERS_proc(PTIMERCONTEXT pTimerContext)
{
    PTIMERINFO pTimerInfo = pTimerContext->pTimerList;
    PTIMERINFO pTimerInfoNext=NULL;   
    
    //Process active timers
    while (pTimerInfo)
    {
        if (pTimerInfo->timeRemaining > pTimerContext->resolution)
            pTimerInfo->timeRemaining -= pTimerContext->resolution;
        else
            pTimerInfo->timeRemaining = 0;

        pTimerInfoNext = pTimerInfo->Next;

        if (pTimerInfo->timeRemaining == 0)
        {
            pTimerInfo->state = TIMER_OFF;

            // Call timer callback
            pTimerInfo->timerCb(pTimerInfo->Param);

            // If timer was not restarted - remove it from the list
            if(pTimerInfo->state == TIMER_OFF)
                TIMERS_RemoveTimerFromList( pTimerContext,pTimerInfo);
        }

        // Get next timer
        pTimerInfo = pTimerInfoNext;
    }

    
}

